package alp.starcode.edms.framework.interceptor;

import alp.starcode.common.web.result.Result;
import alp.starcode.edms.framework.constant.Constant;
import alp.starcode.edms.framework.constant.ResultCode;
import alp.starcode.edms.framework.exception.UserException;
import alp.starcode.edms.framework.model.UserSession;
import alp.starcode.edms.framework.model.vo.UserVo;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Arrays;
import java.util.List;

@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Value("${oauth2.check-token-url:}")
    private String checkTokenUrl;

    @Value("${oauth2.login-page-url:}")
    private String loginPageUrl;

    @Value("${oauth2.get-user-info-url:}")
    private String getUserInfoUrl;

    @Autowired
    private RestTemplate restTemplate;

    // 不需要登录就能访问的接口
    private static final List<String> EXCLUDE_PATHS = Arrays.asList(
            "/imageAsset/download/**",
            "/appAsset/download/**",
            "/login/logout.do",
            "/trust/chain/addBaselineValue",
            "/trust/chain/getBaselineParams",
            "/trust/chain/addMetricCollect",
            "/remote-script/execute",
            "/remote-script/tpm2-attestation",
            "/remote-script/receive-tpm2-files",
            "/remote-script/verify-tpm2-quote",
            "/remote-script/test1234",
            "/doc.html",
            "/webjars/**",
            "/swagger-resources/**",
            "/favicon.ico"
    );

    private boolean checkToken(String token) {
        try {
            if(StringUtils.isBlank(token) || "null".equals(token)) {
                return Boolean.FALSE;
            }
            String url = checkTokenUrl + "?token=" + token;
            ResponseEntity<String> tokenResponse = restTemplate.getForEntity(url, String.class);
            if(tokenResponse.getStatusCode() == HttpStatus.OK) {
                JSONObject entries = JSONUtil.parseObj(tokenResponse.getBody());
                Integer code = entries.getInt("code");
                if(code == 0) {
                    return Boolean.TRUE;
                } else {
                    log.error("登录Token验证失效，{}", url);
                    return Boolean.FALSE;
                }
            } else {
                log.error("登录Token验证异常，{}", url);
                return Boolean.FALSE;
            }
        } catch (Exception e) {
            log.error("登录Token验证异常，{}", e.getMessage());
            return Boolean.FALSE;
        }
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String requestURI = request.getRequestURI();
//        System.out.println("token: " + request.getHeader(Constant.TOKEN));
//        System.out.println(requestURI);
        if(requestURI.endsWith(".js") || requestURI.endsWith("css")) {
            return Boolean.TRUE;
        }

        if(EXCLUDE_PATHS.stream().anyMatch(path -> {
            if(path.endsWith("/**")) {
                return requestURI.startsWith(path.substring(0, path.length() - 3));
            }
            return requestURI.equals(path);
        })) {
            return Boolean.TRUE;
        }

        String token = request.getHeader(Constant.TOKEN);
        if(StringUtils.isBlank(token) || "null".equals(token)) {
            Result.response(ResultCode.TOKEN_FAIL_OR_EXPIRE, loginPageUrl, response);
            return Boolean.FALSE;
        }

        if(!checkToken(token)) {
            Result.response(ResultCode.TOKEN_FAIL_OR_EXPIRE, loginPageUrl, response);
            return Boolean.FALSE;
        }

        UserVo userVo = getUserInfo(token);
        // 缓存用户信息
        // 转换为VO
        UserSession userSession = new UserSession();
        BeanUtil.copyProperties(userVo, userSession);
        // 将用户信息存入session
        HttpSession session = request.getSession();
        session.setAttribute("userSession", userSession);
        return Boolean.TRUE;
    }

    public UserVo getUserInfo(String token) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.setBearerAuth(token);

        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);
        ResponseEntity<String> exchange = restTemplate.exchange(getUserInfoUrl, HttpMethod.GET, requestEntity, String.class);
        if(exchange.getStatusCode() == HttpStatus.OK) {
            JSONObject entries = JSONUtil.parseObj(exchange.getBody());
            Integer code = entries.getInt("code");
            if (code == 0) {
                JSONObject data = entries.getJSONObject("data");
                JSONObject sysUser = data.getJSONObject("sysUser");
                UserVo userVo = new UserVo();
                userVo.setId(sysUser.getStr("userId"));
                userVo.setUsername(sysUser.getStr("name"));
                userVo.setName(sysUser.getStr("name"));
                return userVo;
            } else {
                log.error("调用认证服务code码异常: {}", code);
                throw new UserException("用户信息查询code码异常");
            }
        } else {
            log.error("调用认证服务状态码异常: {}", exchange.getStatusCode());
            throw new UserException("调用认证服务状态码异常");
        }
    }

}